
from fglex import *

class Machine4:
    def __init__(self, debug_max=1000, ext=None):
        self.stack = []
        self.stackb = []
        self.acc = 0
        self.debug_max = debug_max
        self.ext = ext


    def push(self, x):
        if isinstance(x, (int, float)):
           self.stack.append(x)
           self.debug_tick(self.stack_show2([len(self.stack) - 1]))
        elif isinstance(x, list):
           self.stack.append(x)
           self.debug_tick(self.stack_show2([len(self.stack) - 1]))
        elif isinstance(x, str):
            # 压指令和执行算同一步
            kw = kd[x]
            kw.f(self)
        else:
            raise Exception(f"Unknow push {x}")

    # ~ def pop(self):
        # ~ exit('hi')
        # pop都是被指令调用

    @property
    def stack_show(self):
        l = []
        for x in self.stack:
            if isinstance(x, list):
                l.append("<code>")
            else:
                l.append(str(x))
        l2 = []
        for x in self.stackb:
            if isinstance(x, list):
                l2.append("<code>")
            else:
                l2.append(str(x))

        return "[{}]_[{}]".format(", ".join(l), ", ".join(l2))

    def stack_show2(self, ix, iy=[]):
        l = []
        for i, x in enumerate(self.stack):
            if isinstance(x, list):
                l.append("<code>")
            else:
                l.append(str(x))
            if i in ix:
                l[-1] = "({})".format(l[-1])
        l2 = []
        for i, x in enumerate(self.stackb):
            if isinstance(x, list):
                l2.append("<code>")
            else:
                l2.append(str(x))
            if i in iy:
                l2[-1] = "({})".format(l2[-1])

        return "[{}]__[{}]".format(", ".join(l), ", ".join(l2))

    def debug_tick(self, tip):
        self.acc += 1
        if self.debug_max <= 0:
            pass
        elif self.acc >= self.debug_max:
            raise Exception('debug limit')
        else:
            print(str(self.acc).ljust(4), tip)



kd = {}
class KeyWord:
    def __init__(self, name):
        self.name = name
        self.f = None
        kd[name] = self

    def bind(self, f):
        self.f = f

def q(name):
    kw = KeyWord(name)
    return kw.bind

@q("+")
def add(m):
    b = m.stack.pop()
    a = m.stack.pop()
    m.stack.append((a + b) % 16)
    m.debug_tick(m.stack_show2([len(m.stack) - 1])+' +')

@q("-")
def sub(m):
    b = m.stack.pop()
    a = m.stack.pop()
    m.stack.append((a - b) % 16)
    m.debug_tick(m.stack_show2([len(m.stack) - 1]) + ' -')

@q("/")  #获得高位/和8作按位与
def ge(m):
    a = m.stack.pop()
    m.stack.append((a // 8) * 8)
    m.debug_tick(m.stack_show2([len(m.stack) - 1]) + ' /')



@q("if")
def if_(m):
    b = m.stack.pop()
    a = m.stack.pop()
    m.debug_tick("judge "+str(a))
    if a != 0:
        for x in b:
            m.push(x)

@q("if-else")
def if_else(m):
    c = m.stack.pop()
    b = m.stack.pop()
    a = m.stack.pop()
    m.debug_tick("judge2 "+str(a))
    if a != 0:
        for x in b:
            m.push(x)
    else:
        for x in c:
            m.push(x)

@q("while-do")
def while_do(m):
    b = m.stack.pop()
    a = m.stack.pop()
    while True:
        m.debug_tick("repeating")
        for x in a:
            m.push(x)

        r = m.stack.pop()
        m.debug_tick("judge repeat"+str(r))
        if r == 0:
            #print("repeat-end")
            break
        else:
            for x in b:
                m.push(x)




@q("over")
def over(m):
    m.stack.append(m.stack[-1])
    m.debug_tick(m.stack_show2([len(m.stack) - 2, len(m.stack) - 1]) + " over")


@q("swap")
def swap(m):
    m.stack[- 2], m.stack[-1] = m.stack[-1], m.stack[- 2]
    m.debug_tick(m.stack_show2([len(m.stack) - 2, len(m.stack) -1]) + " swap")


@q("drop")
def drop(m):
    x = m.stack.pop()
    if isinstance(x, list):
        s = "<code>"
    else:
        s = str(x)
    m.debug_tick(m.stack_show2([]) + " drop " + s)

@q("call")
def call(m):
    a = m.stack.pop()
    m.debug_tick("calling")
    for x in a:
        m.push(x)
    #print("call-end")

@q("dump")
def dump(m):
    a = m.stack.pop()
    m.stackb.append(a)
    if isinstance(a, list):
        s = "<code>"
    else:
        s = str(a)
    m.debug_tick(m.stack_show + " dump " + s)

@q("load")
def load(m):
    a = m.stackb.pop()
    m.stack.append(a)
    if isinstance(a, list):
        s = "<code>"
    else:
        s = str(a)
    m.debug_tick(m.stack_show + " load " + s)

@q("ext")
def ext(m):
    if m.ext is not None:
        m.debug_tick("extern call")
        m.ext(self.stackb)




def run(s, debug_max=1000):
    l = scan(s)
    m = Machine4(debug_max)
    for x in l:
        m.push(x)
    r = m.stack_show
    print(":", r)


__all__ = ["run"]

if __name__ == "__main__":
    import sys
    args = sys.argv
    if len(args) == 2:
        try:
            with open(args[1], encoding="utf8") as f:
                s = f.read()
        except Exception:
            print("can't open", args[1])
            exit()
        run(s, 20000)
    elif len(args) == 3:
        if args[1] == "-o":
            try:
                with open(args[2], encoding="utf8") as f:
                    s = f.read()
            except Exception:
                print("can't open", args[1])
                exit()
            run(s, -1)








